%% Author: Zhuruev Konstantin
%% Created: Nov 16, 2011
%% Description: Chord Simulator
-module(chord).

%% Defines
-define(StabilizationTime, 10).

%% Exported Functions
-compile( export_all ).

start() -> "Chord Simulation" = "Chord Simulation",
		   
		   random_seed_start(),
		   
		   create().

node( Predeccessor, Successor, Keys_Ids_List ) ->
	
	% Start stabilization whithin 10 sec
	Random_Time2Stabilization = random:uniform(1000 * ?StabilizationTime),
	
	receive
	
		{stop} -> 
				node_stop;
		
		{find_Successor, Pid} -> 
				
				%% Update Node connected to you with his new successor
				Pid ! {update_Successor, self()},
				node( Pid, Successor, Keys_Ids_List ); 
		
		{notify, Pid} ->
				node (notify(Predeccessor, Pid), Successor, Keys_Ids_List);	
			
		{return_Predeccessor, Pid} ->
				Pid ! Predeccessor,				
				node( Predeccessor, Successor, Keys_Ids_List );
		
		{update_Predeccessor, New_Predeccessor} -> 
				node ( New_Predeccessor, Successor, Keys_Ids_List );
		
		{update_Successor, New_Successor} ->
			    node ( Predeccessor, New_Successor, Keys_Ids_List )
	
	after Random_Time2Stabilization ->
				io:format("Node ~p Running Stabilization after ~p msec ~n", [self(), Random_Time2Stabilization]),
				stabilize( Successor ),
				node( Predeccessor, Successor, Keys_Ids_List )
	
	end.

%%
%% Stabilization protocol contains 6 functions
%%

%% Create Chord Ring
create()      		 -> 
	
	% Create new node
	MyPid =
	spawn(chord,
		  node,
		  [[], 		%% Predeccessor is empty
		   [],  	%% We use h(node) = ID as successor
		   []]),	%% Chord ring created without any files

	MyPid ! {update_Successor, MyPid},
	MyPid.	%% Return Chord's first Pid
	
%% Join Chord Ring
join( Node ) -> 

	% Create new node
	MyPid =
	spawn(chord,
		  node,
		  [[], 		%% Predeccessor is empty
		   [],  	%% We use h(node) = ID as successor
		   []]),	%% Chord ring created without any files
	
	Node ! {find_successor, MyPid},
	
	MyPid. %% Return new node's Pid

%% Each time node n runs stabilize(), 
%% it asks its successor for the successor's predecessor p, 
%% and decides whether p should be n's successor instead

stabilize( Successor ) -> 
	
	case is_pid(Successor) and is_process_alive(Successor) of
	
		true  ->
			Successors_Predeccessor = Successor ! {return_Predeccessor, self()},
			
			case Successor =:= Successors_Predeccessor of
				true  -> no_need_to_update_my_successor;
				false -> self() ! {update_Successor, Successors_Predeccessor},
						 Successors_Predeccessor ! {notify, self()}
			end;

		false ->
			pid_is_null
	end.


%% stabilize() notifies node n's successor of n's existence, 
%% giving the successor the chance to change its predecessor to n

notify( MyPredeccessor, Proposed_Predeccesor )  -> 
	case  MyPredeccessor =:= Proposed_Predeccesor of
		true  -> ok;
		false -> Proposed_Predeccesor
	end.


fix_fingers() 		 -> ok.
check_predeccessor() -> ok.

%% Random seed generator
random_seed_start() ->
	case random:seed(now()) of
	
		undefined -> random_seed_start();
		
		_Else 	  -> ok
	
	end.

%% Simple Hash Func: SHA1
hash(Data) -> crypto:sha(Data).